package com.acegear.horizon.domain.models;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

import com.acegear.horizon.ApplicationContextProvider;
import com.acegear.horizon.domain.events.NewQAEvent;
import com.acegear.horizon.domain.events.QAType;
import com.acegear.horizon.domain.events.processers.EventProcessor;
import com.acegear.horizon.domain.models.constraint.ClubEventState;
import com.acegear.horizon.domain.models.jpa.ClubBase;
import com.acegear.horizon.domain.models.jpa.ClubEventBase;
import com.acegear.horizon.domain.models.jpa.ClubEventInfo;
import com.acegear.horizon.domain.models.jpa.ClubEventItem;
import com.acegear.horizon.domain.models.jpa.ClubEventMember;
import com.acegear.horizon.domain.models.jpa.ClubEventQA;
import com.acegear.horizon.domain.models.jpa.ClubEventStage;
import com.acegear.horizon.domain.models.jpa.ClubInfo;
import com.acegear.horizon.domain.models.jpa.EventLike;
import com.acegear.horizon.domain.models.jpa.EventOrder;
import com.acegear.horizon.domain.models.jpa.OrderItem;
import com.acegear.horizon.domain.models.vo.ClubEventDetailVO;
import com.acegear.horizon.domain.models.vo.ClubEventReleaseVO;
import com.acegear.horizon.domain.models.vo.ClubEventVO;
import com.acegear.horizon.domain.models.vo.EventHostVO;
import com.acegear.horizon.domain.repository.jpa.ClubBaseRepository;
import com.acegear.horizon.domain.repository.jpa.ClubEventBaseRepository;
import com.acegear.horizon.domain.repository.jpa.ClubEventInfoRepository;
import com.acegear.horizon.domain.repository.jpa.ClubEventItemGroupRepository;
import com.acegear.horizon.domain.repository.jpa.ClubEventItemRepository;
import com.acegear.horizon.domain.repository.jpa.ClubEventMemberRepository;
import com.acegear.horizon.domain.repository.jpa.ClubEventQARepository;
import com.acegear.horizon.domain.repository.jpa.ClubEventStageRepository;
import com.acegear.horizon.domain.repository.jpa.ClubInfoRepository;
import com.acegear.horizon.domain.repository.jpa.ContentPartRepository;
import com.acegear.horizon.domain.repository.jpa.EventLikeRepository;
import com.acegear.horizon.domain.repository.jpa.EventOrderRepository;
import com.acegear.horizon.domain.repository.jpa.UserInfoRepository;

import javaslang.Tuple;
import javaslang.Tuple2;

/**
 * Created by wangsike on 2016/12/21.
 */
public class ClubEvent {

    private ClubEventBase eventBase;

    private List<ClubEventStage> eventStages;

    private List<ClubEventQA> eventQAS;

    private ClubEventInfo eventInfo;

    private ClubEventBaseRepository clubEventBaseRepository;

    private ClubEventStageRepository clubEventStageRepository;

    private ClubEventMemberRepository clubEventMemberRepository;

    private ClubEventItemGroupRepository clubEventItemGroupRepository;

    private ClubEventItemRepository clubEventItemRepository;

    private ClubEventInfoRepository clubEventInfoRepository;

    private ClubBaseRepository clubBaseRepository;

    private ClubInfoRepository clubInfoRepository;

    private ClubEventQARepository clubEventQARepository;

    private EventOrderRepository eventOrderRepository;

    private UserInfoRepository userInfoRepository;

    private EventLikeRepository eventLikeRepository;

    private ModelFactory modelFactory;

    private EventProcessor eventProcessor;

    private ContentPartRepository contentPartRepository;

    public ClubEvent() {
        init();
    }

    public ClubEvent(ClubEventBase eventBase) {
        this.eventBase = eventBase;
        init();
    }

    public ClubEvent(ClubEventInfo eventInfo, ClubEventBase eventBase) {
        this.eventInfo = eventInfo;
        this.eventBase = eventBase;
        init();
    }

    private void init() {
        clubEventBaseRepository = ApplicationContextProvider.getBean(ClubEventBaseRepository.class);

        clubEventStageRepository = ApplicationContextProvider.getBean(ClubEventStageRepository.class);

        clubEventMemberRepository = ApplicationContextProvider.getBean(ClubEventMemberRepository.class);

        clubEventItemGroupRepository = ApplicationContextProvider.getBean(ClubEventItemGroupRepository.class);

        clubEventItemRepository = ApplicationContextProvider.getBean(ClubEventItemRepository.class);

        clubEventInfoRepository = ApplicationContextProvider.getBean(ClubEventInfoRepository.class);

        clubBaseRepository = ApplicationContextProvider.getBean(ClubBaseRepository.class);

        clubInfoRepository = ApplicationContextProvider.getBean(ClubInfoRepository.class);

        clubEventQARepository = ApplicationContextProvider.getBean(ClubEventQARepository.class);

        eventOrderRepository = ApplicationContextProvider.getBean(EventOrderRepository.class);

        userInfoRepository = ApplicationContextProvider.getBean(UserInfoRepository.class);

        eventLikeRepository = ApplicationContextProvider.getBean(EventLikeRepository.class);

        modelFactory = ApplicationContextProvider.getBean(ModelFactory.class);

        eventProcessor = ApplicationContextProvider.getBean("eventProcessor", EventProcessor.class);

        contentPartRepository = ApplicationContextProvider.getBean(ContentPartRepository.class);
    }
    
    public void saveClubEventReleaseVO(ClubEventReleaseVO releaseVO) {
    	ClubEventBase eventBase = releaseVO.getEventBase();
    	clubEventBaseRepository.save(eventBase);
    	System.out.println("save ClubEventBase success!!!");
    	
    	List<ClubEventStage> eventStages = releaseVO.getEventStages();
    	for (ClubEventStage stage : eventStages) {
    		clubEventStageRepository.save(stage);
    	}
    	System.out.println("save eventStages success!!!");
    	
    	List<ClubEventQA> eventQAS = releaseVO.getEventQAS();
    	for (ClubEventQA qa : eventQAS) {
    		clubEventQARepository.save(qa);
    	}
    	System.out.println("save eventQAS success!!!");
    	
//    	ClubEventInfo eventInfoHandlers = releaseVO.getEventInfoHandlers();
//    	clubEventInfoRepository.save(eventInfoHandlers);
//    	System.out.println("save eventInfoHandlers success!!!");
    }

    public ClubEventBase getEventBase() {
        return eventBase;
    }

    public List<ClubEventMember> getEventMembers(Pageable pageable) {
        return clubEventMemberRepository.findByEventId(getClubEventId());
    }

    public Long getClubEventId() {
        return eventBase.getClubEventId();
    }

    public void joinEvent(Long userId, String userAvatar) {
        ClubEventMember clubEventMember = new ClubEventMember(getClubEventId(), userId, userAvatar);
        clubEventMemberRepository.save(clubEventMember);
    }

    public List<ClubEventStage> getEventStages() {
        return clubEventStageRepository.findByEventId(getClubEventId());
    }

    public void setEventStages(List<ClubEventStage> eventStages) {
        eventStages.forEach(clubEventStage -> clubEventStage.setEventId(getClubEventId()));
        this.eventStages = (List<ClubEventStage>) clubEventStageRepository.save(eventStages);
    }

    public Optional<ClubEventInfo> getEventInfo() {
        return clubEventInfoRepository.findByEventId(getClubEventId());
    }

    public void setEventInfo(ClubEventInfo eventInfo) {
        eventInfo.setEventId(getClubEventId());
        this.eventInfo = clubEventInfoRepository.save(eventInfo);
    }

    public ClubEventInfo getEventInfoHandlers() {
        return eventInfo;
    }

    public ClubEventVO getEventVO() {
        ClubBase owner = clubBaseRepository.findByClubId(eventBase.getOwnerId()).get();

        List<String> sponsorLogos;

        if (eventBase.getSponsors() == null || eventBase.getSponsors().isEmpty()) {
            sponsorLogos = new ArrayList<>();
        } else {
            sponsorLogos = clubBaseRepository
                    .findByClubIds(eventBase.getSponsors())
                    .stream()
                    .map(ClubBase::getLogo)
                    .collect(Collectors.toList());
        }

        eventBase.getState();
        save();
        return new ClubEventVO(getClubEventId(),
                eventBase.getName(),
                eventBase.getCover(),
                owner.getClubId(),
                owner.getClubName(),
                owner.getLogo(),
                sponsorLogos,
                eventBase.getCreateAt(),
                getMemberCount(),
                getLikeCount(),
                eventBase.getAddress(),
                eventBase.getState(),
                eventBase.getStartAt(),
                eventBase.getEndAt());
    }

    public Integer getLikeCount() {
        return eventLikeRepository.countByEventId(getClubEventId());
    }

    public Integer getMemberCount() {
        return clubEventMemberRepository.countByEventId(getClubEventId());
    }

    public ClubEventDetailVO getEventDetailVO(Long viewerId) {
        ClubBase owner = clubBaseRepository.findByClubId(eventBase.getOwnerId()).get();
        Boolean isLike = eventLikeRepository.findTopByUserIdAndEventId(viewerId, getClubEventId()).isPresent();
        eventBase.getState();
        save();
        return new ClubEventDetailVO(getClubEventId(),
                eventBase.getName(),
                eventBase.getCover(),
                owner.getClubId(),
                owner.getClubName(),
                owner.getLogo(),
                eventBase.getStartAt(),
                eventBase.getEndAt(),
                eventBase.getSummary(),
                clubEventMemberRepository.countByEventId(getClubEventId()),
                eventBase.getServicePhone(),
                isLike,
                clubEventMemberRepository
                        .findByEventId(getClubEventId())
                        .stream()
                        .map(ClubEventMember::getUserAvatar)
                        .collect(Collectors.toList()),
                eventBase.getAddress(),
                eventBase.getLocation(),
                eventBase.getState(),
                eventBase.isDeleted());
    }

    public ClubEventItem getEventItem(Long itemId) {
        return clubEventItemRepository.findByItemId(itemId).orElse(null);
    }

    public void createQA(Long askUserId, String question) {
        Long eventQAId = modelFactory.nextQAId();
        ClubEventQA qa = new ClubEventQA(getClubEventId(), eventQAId, askUserId, question, "");
        clubEventQARepository.save(qa);
        publishQACreated(qa);
    }

    public Boolean like(Long userId) {
        return userInfoRepository
                .findByUserId(userId)
                .map(userInfo -> eventLikeRepository.save(new EventLike(getClubEventId(), userId)))
                .isPresent();
    }

    public boolean unlike(Long userId) {
        eventLikeRepository
                .deleteByEventIdAndUserId(getClubEventId(), userId);
        return true;
    }


    public List<ClubEventQA> getQA(Pageable pageable) {
        return clubEventQARepository.findByEventIdAndAnswered(getClubEventId(), true, pageable);
    }
    
    public List<ClubEventQA> getEventQAS() { // TreeJohn
        return clubEventQARepository.findByEventIdAndAnswered(getClubEventId(), true);
    }

    public void setEventQAS(List<ClubEventQA> eventQAS) {
        this.eventQAS = eventQAS;
    }

    public Map<String, List<EventHostVO>> getHosts() {
        ClubBase owner = clubBaseRepository.findByClubId(eventBase.getOwnerId()).get();
        ClubInfo ownerInfo = clubInfoRepository.findByClubId(eventBase.getOwnerId()).get();
        Map<String, List<EventHostVO>> hosts = new HashMap<>();
        List<EventHostVO> clubOwner = new ArrayList<EventHostVO>() {
            {
                add(new EventHostVO(owner, ownerInfo));
            }
        };
        List<EventHostVO> sponsors = clubBaseRepository
                .findByClubIds(eventBase.getSponsors())
                .stream()
                .map(clubBase -> {
                    ClubInfo clubInfo = clubInfoRepository.findByClubId(clubBase.getClubId()).get();
                    return new EventHostVO(clubBase, clubInfo);
                })
                .collect(Collectors.toList());
        hosts.put("owner", clubOwner);
        hosts.put("sponsors", sponsors);
        return hosts;
    }

    public Boolean deductOrder(String orderId, Long userId, List<OrderItem> orderItems) {
        Boolean canDeduct = true;
        if (eventBase.getState() == ClubEventState.END) return false;
        List<Tuple2<ClubEventItem, Integer>> eventItems = new ArrayList<>();
        for (OrderItem orderItem : orderItems) {
            ClubEventItem eventItem = clubEventItemRepository.findByItemId(orderItem.getItemId()).get();
            canDeduct = canDeduct && eventItem.canDeduct(orderItem.getCount());
            eventItems.add(Tuple.of(eventItem, orderItem.getCount()));
        }
        if (canDeduct) {
            eventItems.forEach(tuple2 -> tuple2._1.deduct(tuple2._2));
            try {
                clubEventItemRepository.save(
                        eventItems
                                .stream()
                                .map(Tuple2::_1)
                                .collect(Collectors.toList()));
                eventOrderRepository.save(new EventOrder(getClubEventId(), orderId, userId, orderItems));
                return true;
            } catch (Throwable t) {
                t.printStackTrace();
                return false;
            }
        } else {
            return false;
        }
    }

    public Boolean undoOrder(String orderId) {
        Optional<EventOrder> eventOrderOptional = eventOrderRepository.findByOrderId(orderId);
        List<ClubEventItem> eventItems = new ArrayList<>();
        if (eventOrderOptional.isPresent()) {
            EventOrder eventOrder = eventOrderOptional.get();
            for (OrderItem orderItem : eventOrder.getItems()) {
                ClubEventItem clubEventItem = clubEventItemRepository.findByItemId(orderItem.getItemId()).get();
                clubEventItem.undo(orderItem.getCount());
                eventItems.add(clubEventItem);
            }
            try {
                clubEventItemRepository.save(eventItems);
                return true;
            } catch (Throwable t) {
                t.printStackTrace();
                return false;
            }
        } else {
            return false;
        }
    }

    public ClubEvent save() {
        eventBase = clubEventBaseRepository.save(eventBase);
        if (eventInfo != null) {
            eventInfo = clubEventInfoRepository.save(eventInfo);
        }
        if (eventStages != null && !eventStages.isEmpty()) {
            eventStages = (List<ClubEventStage>) clubEventStageRepository.save(eventStages);
        }
        if (eventQAS != null && !eventQAS.isEmpty()) {
            eventQAS = (List<ClubEventQA>) clubEventQARepository.save(eventQAS);
        }
        return this;
    }

    public void update(ClubEvent clubEvent) {

        ClubEventBase clubEventBase = clubEvent.getEventBase();
        ClubEventInfo clubEventInfo = clubEvent.getEventInfoHandlers();

        eventBase.setName(clubEventBase.getName());
        eventBase.setState(clubEventBase.getState());
        eventBase.setType(clubEventBase.getType());
        eventBase.setCover(clubEventBase.getCover());
        eventBase.setEndAt(clubEventBase.getEndAt());
        eventBase.setStartAt(clubEventBase.getStartAt());
        eventBase.setSummary(clubEventBase.getSummary());
        eventBase.setServicePhone(clubEventBase.getServicePhone());
        eventBase.setDeleted(clubEventBase.isDeleted());
        eventBase.getLocation().update(clubEventBase.getLocation());

        eventBase.getAddress().update(clubEventBase.getAddress());

        eventInfo.setIntroduction(clubEventInfo.getIntroduction());

        save();
    }

    public Optional<ClubEventQA> getQA(Long qaId) {
        return clubEventQARepository.findTopByEventQAId(qaId);
    }

    private void publishQACreated(ClubEventQA clubEventQA) {
        eventProcessor.publish(new NewQAEvent(clubEventQA.getEventQAId(),
                clubEventQA.getEventId(),
                QAType.CLIENT,
                clubEventQA.getQuestion(),
                clubEventQA.getAnswer(),
                clubEventQA.getAskUserId()));
    }
}
